package so.sao.shop.supplier.service.impl;

import com.github.pagehelper.PageInfo;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import so.sao.shop.supplier.config.Constant;
import so.sao.shop.supplier.dao.CheckingAccountAlipayDao;
import so.sao.shop.supplier.dao.CheckingAccountResultDao;
import so.sao.shop.supplier.dao.PurchaseDao;
import so.sao.shop.supplier.dao.PurchaseItemDao;
import so.sao.shop.supplier.domain.*;
import org.springframework.transaction.annotation.Transactional;
import so.sao.shop.supplier.config.CommConstant;
import so.sao.shop.supplier.dao.*;
import so.sao.shop.supplier.domain.*;
import so.sao.shop.supplier.pojo.Result;
import so.sao.shop.supplier.pojo.input.CheckOrderSearchInput;
import so.sao.shop.supplier.pojo.output.CheckingAccountOrder;
import so.sao.shop.supplier.pojo.vo.CheckingAccountAlipayVo;
import so.sao.shop.supplier.service.CheckingAccountResultService;
import so.sao.shop.supplier.util.*;
import so.sao.shop.supplier.util.DateUtil;
import so.sao.shop.supplier.util.ExcelReader;
import so.sao.shop.supplier.util.IPUtil;
import so.sao.shop.supplier.util.Ognl;
import sun.java2d.pipe.SpanShapeRenderer;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>Version: supplier V1.2.0 </p>
 * <p>Title: CheckingAccountResultServiceImpl</p>
 * <p>Description: 对账结果</p>
 *
 * @author: fangzhou
 * @Date: Created in 2017/11/22 10:43
 */
@Service
public class CheckingAccountResultServiceImpl implements CheckingAccountResultService {

    /**
     * 初始化日志
     */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 订单表对应的DAO（PurchaseDao）
     */
    @Autowired
    private PurchaseDao purchaseDao;

    /**
     * 订单详情表对应的DAO
     */
    @Autowired
    private PurchaseItemDao purchaseItemDao;

    /**
     * 阿里数据表对应的Dao
     */
    @Autowired
    private CheckingAccountAlipayDao alipayDao;

    /**
     * 对账记录表对应的DAO
     */
    @Autowired
    private CheckingAccountRecordDao recordDao;

    /**
     * 对账结果表对应的DAO
     */
    @Autowired
    private CheckingAccountResultDao resultDao;

    /**
     * 对账支付信息表
     */
    @Autowired
    private CheckingAccountPaymentDao paymentDao;

    /**
     * 对账结果表对应的Dao
     */
    @Autowired
    private CheckingAccountResultDao checkingAccountResultDao;

    /**
     * 根据订单编号查询订单的支付记录及阿里对账数据的支付记录
     *
     * @param orderId 订单编号
     * @return
     */
    @Override
    public Result findPaymentByOrderId(String orderId) {
        /*
            1.根据订单ID查询payId和订单的支付时间，支付金额
            2.根据orderId查询子订单退款情况
            3.根据payId查询阿里支付记录
            4.判断是否存在退款,若只存在一条记录,那么不存在退款，若有多条记录，则存在退款
            5.多条记录时候，因为倒叙查询的关系，故最后一条记录为支付记录，其余为退款记录
         */
        // 存放阿里支付退款记录和平台支付退款记录的map
        Map<String,Object> map = new HashMap<>(2);
        // 根据订单ID查询payId和订单的支付时间，支付金额
        Purchase purchase= purchaseDao.findById(orderId);
        if (Ognl.isNull(purchase)){
            return Result.fail("该订单不存在");
        }
        logger.info("订单号:{}",orderId);
        logger.info("支付时间:{}",DateUtil.getStringDatePrecision(purchase.getOrderPaymentTime()));
        logger.info("支付金额:{}",purchase.getPayAmount());
        logger.info("PayID（商户订单号）:{}",purchase.getPayId());
        // 平台支付退款记录信息
        Map<String,Object> paymentMap = new HashMap<>(5);
        // 支付完成时间
        paymentMap.put("paymentTime",DateUtil.getStringDatePrecision(purchase.getOrderPaymentTime()));
        // 订单号
        paymentMap.put("orderId",orderId);
        // 实付金额
        paymentMap.put("paymentMoney",purchase.getPayAmount());
        // 订单流水号
        paymentMap.put("orderPaymentNum",purchase.getOrderPaymentNum());
        // 根据orderId查询子订单退款情况
        List<PurchaseItem> list = purchaseItemDao.getReturnInfoByOrderId(orderId);
        if (Ognl.CollectionIsNotEmpty(list)){
            // 组装退款记录
            List returnList = new ArrayList();
            for (PurchaseItem purchaseItem:list) {
                logger.info("订单详情号:{}",purchaseItem.getDetailsId());
                logger.info("退款金额:{}",purchaseItem.getReturnMoney());
                if (Ognl.isNull(purchaseItem.getReturnMoneyTime())){
                    continue;
                }else {
                    logger.info("退款时间:{}",DateUtil.getStringDatePrecision(purchaseItem.getReturnMoneyTime()));
                    Map<String,Object> returnMap = new HashMap<>(2);
                    returnMap.put("drawbackTime",DateUtil.getStringDatePrecision(purchaseItem.getReturnMoneyTime()));
                    returnMap.put("drawbackPrice",purchaseItem.getReturnMoney());
                    returnList.add(returnMap);
                }
            }
            // 退款数据
            if (!Ognl.CollectionIsNotEmpty(returnList)){
                paymentMap.put("list",null);
            }else {paymentMap.put("list",returnList);}
        }else {
            paymentMap.put("list",null);
        }
        map.put("payment",paymentMap);
        // 根据payId查询阿里支付记录
        List<CheckingAccountAlipay> alipayList = alipayDao.findByPayId(purchase.getPayId());
        if (Ognl.CollectionIsNotEmpty(alipayList)){
            // 阿里数据
            Map<String,Object> AlipayMap = new HashMap<>(5);
            // 获取支付信息
            CheckingAccountAlipay checkingAccountAlipay = alipayList.get(alipayList.size()-1);
            // 商户订单号payId
            AlipayMap.put("payId",purchase.getPayId());
            // 支付时间
            AlipayMap.put("paymentTime",DateUtil.getStringDatePrecision(checkingAccountAlipay.getEndTime()));
            // 商家实收金额
            AlipayMap.put("paymentMoney",checkingAccountAlipay.getPaidInMoney());
            // 支付流水号
            AlipayMap.put("orderPaymentNum",checkingAccountAlipay.getPaymentNum());
            // 判断是否存在退款,若只存在一条记录,那么不存在退款，若有多条记录，则存在退款
            List aliReturnList = new ArrayList();
            if (1 == alipayList.size()){
                AlipayMap.put("list",null);
            }else {
                int len = alipayList.size()-1;
                for (int i = 0; i < len; i++) {
                    Map<String,Object> aliReturnMap = new HashMap<>(2);
                    checkingAccountAlipay = alipayList.get(i);
                    // 退款时间
                    aliReturnMap.put("drawbackTime",DateUtil.getStringDatePrecision(checkingAccountAlipay.getCreateTime()));
                    // 退款金额(取绝对值)
                    aliReturnMap.put("drawbackPrice",(checkingAccountAlipay.getPaidInMoney()).abs());
                    aliReturnList.add(aliReturnMap);
                }
                AlipayMap.put("list",aliReturnList);
            }
            map.put("alipay",AlipayMap);
        } else {
            map.put("alipay",null);
        }
        return Result.success("成功",map);
    }

    /**
     * 根据条件查询订单列表
     * @param checkOrderSearchInput 查询条件
     * @return 返回订单列表
     */
    @Override
    public Result searchOrders(CheckOrderSearchInput checkOrderSearchInput) {

        //起始时间不能大于终止时间
        String timeCheck = DataCompare.createAtCheck(checkOrderSearchInput.getBeginPaymentTime(),checkOrderSearchInput.getEndPaymentTime());
        if(StringUtils.isNotEmpty(timeCheck)){
            return Result.fail(timeCheck);
        }
        //查询字段去空格
        if(StringUtils.isNotEmpty(checkOrderSearchInput.getInputValue())){
            checkOrderSearchInput.setInputValue(checkOrderSearchInput.getInputValue().trim());
        }
        if(StringUtils.isNotEmpty(checkOrderSearchInput.getStoreName())){
            checkOrderSearchInput.setStoreName(checkOrderSearchInput.getStoreName().trim());
        }
        //分页
        PageTool.startPage(checkOrderSearchInput.getPageNum(),checkOrderSearchInput.getPageSize());
        List<CheckingAccountOrder> list = checkingAccountResultDao.findAll(checkOrderSearchInput);
        PageInfo<CheckingAccountOrder> pageInfo = new PageInfo<>(list);
        return Result.success("查询财务订单列表成功：",pageInfo);
    }
    /**
     * 根据对账类型查询对账结果
     * @param checkingType 对账状态
     * @return
     */
    @Override
    public Result selectMatchingRecordByCheckType(Integer checkingType,Integer pageSize,Integer pageNum,Long Id) {
        // 查询用户ID上传的刚对过帐的记录
        List<CheckingAccountRecord> recordList = recordDao.findRecordByCheckerId(Id);
        CheckingAccountRecord checkingAccountRecord=null;
        if(recordList.size()>0){
            //取最近的对账记录
            checkingAccountRecord=recordList.get(0);
        }
        //开始分页
        PageTool.startPage(pageNum,pageSize);
        // 根据最近的已对账记录id查询 最近的对账结果
        List<CheckingAccountResult> checkingAccountResultList=checkingAccountResultDao.selectMatchingRecordByCheckType(checkingType,Id,checkingAccountRecord.getId());
        PageInfo<CheckingAccountResult> pageInfo = new PageInfo<CheckingAccountResult>(checkingAccountResultList);
        return Result.success("查询对账结果成功",pageInfo);
    }



    /**
     * 对账
     *
     * @param checkerId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result checkingAccount(Long checkerId,String tempPath) throws IOException {
        // 下载、解析、校验excel数据，将正确数据写入阿里记录数据库，并更新对账记录表数据
        Result excelResult = checkOutAccountAlipay(checkerId,tempPath);
        if (0 == excelResult.getCode()){
            return excelResult;
        }
        // 取出对账记录信息
        CheckingAccountRecord  record = (CheckingAccountRecord)excelResult.getData();
        // 将订单支付回调信息表中未银货两讫的回调信息写入checking_account_payment
        paymentDao.insertAccountPayment(checkerId,record.getId());
        // 查出阿里有智购平台没有的数据
        List<CheckingAccountAlipay> aliHave = alipayDao.findAliHave(record.getId());
        // 查出阿里无智购平台有的数据
        List<CheckingAccountPayment> paymentHave = paymentDao.findPaymentHave(record.getId());
        // 查出两部分都有的数据
        List<CheckingAccountAlipay> allHave = alipayDao.findAllHave(record.getId());
        // 对出结果
        checking(aliHave,paymentHave,allHave);
        // 查出银货两讫的支付流水号和核帐时间
        List<CheckingAccountResult> PaymentNums =resultDao.findOverPaymentNum(record.getId());
        if (Ognl.CollectionIsNotEmpty(PaymentNums)){
            // 删除结果表自身以前对账未银货两讫但是本次银货两讫的数据
            resultDao.delete(PaymentNums);
            // 更新订单表中银货两讫的数据
            Date time = new Date();
            purchaseDao.updateCheck(PaymentNums,time);
            // 更新支付回掉信息表银货两讫的数据
            purchaseDao.updatePayInfo(PaymentNums,time);
        }
        //返回数据
        return Result.success("对账完成",record);
    }


    /**
     * 下载、解析、校验excel数据，将正确数据写入checking_account_alipay，并将checking_account_record数据更新
     * @param checkerId
     * @param tempPath
     * @return
     * @throws IOException
     */
    private Result checkOutAccountAlipay(Long checkerId,String tempPath) throws IOException{
        logger.info("用户ID:{},对账发起时间:{}",checkerId, new Date());
        // 查询该ID上传的未对过帐的excel文件
        List<CheckingAccountRecord> recordList = recordDao.findByCheckerId(checkerId);
        // 若集合为null,表示未上传excel,若不为null但有多条数据,说明数据有误,只有不为null且只有1条记录时,进行对账
        CheckingAccountRecord accountRecord = null;
        if (Ognl.CollectionIsNotEmpty(recordList)){
            if (1 == recordList.size()){
                // 取到对账记录
                accountRecord = recordList.get(0);
            }else {
                return Result.fail("对账记录表数据有误");
            }
        }else {
            return Result.fail("未查询到要对账的文件,请先上传EXCEL");
        }
        // 获取excel路径
        String excelUrl = accountRecord.getExcelUrl();
        if (Ognl.isNull(excelUrl) || Ognl.isNull(accountRecord.getId())){
            return Result.fail("对账记录表数据有误");
        }
        // 文件名称
        String filename=excelUrl.substring(excelUrl.lastIndexOf("/") + 1,excelUrl.length());
        logger.info("Excel文件url:{},Excel文件名:{}",excelUrl, filename);
        // 批量上传模板表头名称
        String[] titles = CommConstant.EXCEL_CHECKALIPAY_TITLES;
        // 下载、解析excel
        Result excelContent = checkOutExcel(excelUrl,tempPath,filename,titles);
        // 解析excel数据出错，返回失败
        if (0 == excelContent.getCode()){
            return excelContent;
        }
        // Excel数据
        Map<String, Object> map = (Map<String, Object>)excelContent.getData();
        if (Ognl.isNull(map) || map.isEmpty()){
            return Result.fail("EXCEL数据为空,请确认后重新上传");
        }
        // excel解析错误行号
        List<Integer> errornum = (List<Integer>)map.get("errorrowno");
        String errorNo = null;
        // 判断错误数据是否存在,是否超过了一百条
        int isError = 1;
        int maxErrorNum = 100;
        if (Ognl.isNotNull(errornum) && errornum.size() > maxErrorNum){
            return Result.fail("EXCEL中错误数据超过100条,请确认后重新上传");
        }else if (Ognl.isNotNull(errornum) && !errornum.isEmpty()){
            errorNo = errornum.toString();
        }else {isError = 0;}
        // excel总条数
        int totalNum = (int)map.get("totalnum");
        // excel解析成功条数
        int succNum = (int)map.get("succnum");
        // excel解析失败条数
        int failNum = (int)map.get("failnum");
        logger.info("Excel错误行号:{},Excel总条数:{},Excel解析成功条数:{},Excel解析失败条数:{}",errorNo, totalNum,succNum,failNum);
        // Excel正确数据
        Map<Integer, Map<String, String>> mapRights = (Map<Integer, Map<String, String>>)map.get("mapright");
        // 组装数据
        Result rightData = chageToCheckingAccountAlipay(mapRights,titles,accountRecord.getId(),checkerId);
        if (0 == rightData.getCode()){
            return rightData;
        }
        logger.info("验证完所有数据时间:{}",new Date());
        // 获取正确数据的list(其收款类型  0.预收款  1.银货两讫 未知)
        List<CheckingAccountAlipay> alipayList = (List<CheckingAccountAlipay>)rightData.getData();
        logger.debug("正确数据的条数:{}",alipayList.size());
        int n = 1000;
        Integer times = getTimes(alipayList.size(),n);
        for (int i = 0; i < times ; i++) {
            List<CheckingAccountAlipay> sublist=null;
            if((i+1)*n <= alipayList.size()){
                sublist = alipayList.subList(i*n,(i+1)*n);
            }else {
                sublist = alipayList.subList(i*n,alipayList.size());
            }
            // 将excel的正确数据插入数据库
            alipayDao.saveAccountAlipay(sublist);
            logger.debug("将excel的正确数据插入数据库 saveAccountAlipay ===>>>:{}",sublist);
        }
        // 计算插入数据的收款类型
        alipayDao.updateReceiptType(accountRecord.getId());
        // 查询此次插入数据的最小完成时间和最大完成时间
        CheckingAccountAlipayVo alipayVo= alipayDao.findEndTimeByRecordId(accountRecord.getId());
        // 将文件名，最大、最小时间，状态，总条数，成功条数，失败条数，错误行号，匹配时间，更新时间更入记录表
        Date now = new Date();
        accountRecord.setExcelName(filename);
        accountRecord.setMinTime(alipayVo.getMinTime());
        accountRecord.setMaxTime(alipayVo.getMaxTime());
        accountRecord.setIsChecking(1);
        accountRecord.setTotalNum(totalNum);
        accountRecord.setSuccNum(succNum);
        accountRecord.setFailNum(failNum);
        accountRecord.setIsError(isError);
        accountRecord.setErrorRowno(errorNo);
        accountRecord.setMatchTime(now);
        accountRecord.setUpdatedAt(now);
        recordDao.updateById(accountRecord);
        return Result.success("阿里数据同步数据库完成",accountRecord);
    }

    /**
     * 计算循环次数
     * @param size
     * @param i
     * @return
     */
    private Integer getTimes(int size, int i) {
        if(size%i==0){
            return Double.valueOf(Math.floor(size/i)).intValue();
        }else{
            return Double.valueOf(Math.floor(size/i)+1).intValue();
        }
    }

    /**
     * 下载、解析excel并校验excel数据
     * @param excelUrl
     * @param tempPath
     * @return
     */
    private Result checkOutExcel(String excelUrl,String tempPath,String filename,String[] titles) throws IOException{
        // 下载excel
        ExcelReader.downloadExcel(excelUrl,tempPath,filename);
        // 批量上传可以为空的模板表头名称
        String[] isNulltitles = CommConstant.EXCEL_CHECKALIPAY_ISNULLTITLES;
        // 解析excel表头
        Result titleResult = ExcelReader.readExcelTitle(tempPath+"/"+filename,0);
        // 解析excel表头出错，返回失败
        if (0 == titleResult.getCode()){
            return titleResult;
        }
        // 取出excel表头
        String[] excelTitles = (String[])titleResult.getData();
        // 校验表头是否符合要求
        boolean flag = ExcelReader.checkTitles(excelTitles,titles);
        if (!flag){
            return Result.fail("EXCEL表与格式不符,请检查后重新上传");
        }
        //使用工具类 获取Excel内容
        return ExcelReader.readExcelContent(titles,isNulltitles,tempPath + "/"+filename,1);
    }

    /**
     * 将excel数据转为List<CheckingAccountAlipay>
     * @param mapRights
     * @param titles
     * @return
     */
    private Result chageToCheckingAccountAlipay(Map<Integer, Map<String, String>> mapRights,String[] titles,long recordId,Long checkerId){
        if (Ognl.isNull(mapRights) || mapRights.isEmpty()){
            return Result.fail("EXCEL无正确数据,请确认后重新上传");
        }
        // 循环正确数据组成实体类的对象
        List<CheckingAccountAlipay> alipays = new ArrayList<>();
        for (Map.Entry<Integer,Map<String, String>> itmap : mapRights.entrySet()) {
            CheckingAccountAlipay alipay = new CheckingAccountAlipay();
            Date time = new Date();
            // 正确数据行号
            alipay.setRowNum(itmap.getKey());
            // 正确数据
            Map<String, String> rightMap = itmap.getValue();
            // 支付宝交易号
            alipay.setPaymentNum(rightMap.get(titles[0]));
            // 商户订单号
            String payId = rightMap.get(titles[1]);
            alipay.setPayId(payId);
            // 业务类型
            alipay.setBusinessType(rightMap.get(titles[2]));
            // 商品名称
            alipay.setCommodityName(rightMap.get(titles[3]));
            // 创建时间
            alipay.setCreateTime(DateUtil.stringSlashToDate(rightMap.get(titles[4])));
            // 完成时间
            alipay.setEndTime(DateUtil.stringSlashToDate(rightMap.get(titles[5])));
            // 对方账户
            alipay.setIncomeAccount(rightMap.get(titles[6]));
            // 订单金额（元）
            alipay.setOrderMoney(new BigDecimal(rightMap.get(titles[7])));
            // 商家实收（元）
            alipay.setPaidInMoney(new BigDecimal(rightMap.get(titles[8])));
            // 支付宝红包（元）
            alipay.setRedPacket(new BigDecimal(rightMap.get(titles[9])));
            // 集分宝（元）
            alipay.setIntegralMoney(new BigDecimal(rightMap.get(titles[10])));
            // 支付宝优惠（元）
            alipay.setAliCoupon(new BigDecimal(rightMap.get(titles[11])));
            // 商家优惠（元）
            alipay.setAccountCoupon(new BigDecimal(rightMap.get(titles[12])));
            // 券核销金额（元）
            alipay.setCertificateAmount(new BigDecimal(rightMap.get(titles[13])));
            // 券名称
            alipay.setTicketName(rightMap.get(titles[14]));
            // 商家红包消费金额（元）
            alipay.setAccountRedPacket(new BigDecimal(rightMap.get(titles[15])));
            // 卡消费金额（元）
            alipay.setCardMoney(new BigDecimal(rightMap.get(titles[16])));
            // 退款批次号/请求号
            alipay.setRefundId(rightMap.get(titles[17]));
            // 服务费（元）
            alipay.setServiceCharge(new BigDecimal(rightMap.get(titles[18])));
            // 分润（元）
            alipay.setShareBenefit(new BigDecimal(rightMap.get(titles[19])));
            // 备注
            alipay.setRemark(rightMap.get(titles[20]));
            // 创建时间
            alipay.setCreatedAt(time);
            // 更新时间
            alipay.setUpdatedAt(time);
            // 记录id
            alipay.setRecordId(recordId);
            // 对账人id
            alipay.setCheckerId(checkerId);
            // 收款类型： 0.预收款  1.银货两讫
            alipay.setReceiptType(0);
            // 支付信息状态： 0.错误  1.正确
            alipay.setAlipayStutas(1);
            alipays.add(alipay);
        }
        return Result.success("转换成功",alipays);
    }

    /**
     * 阿里数据有，智购平台无的数据进行处理
     * @param pays
     * @return
     */
    private List<CheckingAccountResult> aliExist(List<CheckingAccountAlipay> pays,String paidType,String refundType){
        List<CheckingAccountResult> aliResults = new ArrayList<>();
        // 若查出阿里有，平台无的数据集合不为空，那么进行循环计算
        if (Ognl.CollectionIsNotEmpty(pays)){
            for (CheckingAccountAlipay pay:pays) {
                CheckingAccountResult aliResult = new CheckingAccountResult();
                // 若此数据为支付交易类型
                if (paidType.equals(pay.getBusinessType())){
                    aliResult = aliPayResult(pay);
                }
                // 若此数据为退款类型
                if (refundType.equals(pay.getBusinessType())){
                    aliResult = aliRefundResult(pay);
                }
                // 对账类型： 1.未对账（平台缺失记录）
                aliResult.setCheckingType(1);
                // 收款类型
                aliResult.setReceiptType(0);
                aliResults.add(aliResult);
            }
        }else {
            aliResults = null;
        }
        return aliResults;
    }

    /**
     * 阿里无智购平台有的数据进行处理
     * @param payments
     * @return
     */
    private List<CheckingAccountResult> paymentExist(List<CheckingAccountPayment> payments){
        List<CheckingAccountResult> paymentResults = new ArrayList<>();
        // 若查查出阿里无智购平台有的数据集合不为空，那么进行循环计算
        if (Ognl.CollectionIsNotEmpty(payments)){
            for (CheckingAccountPayment payment:payments) {
                CheckingAccountResult paymentResult = new CheckingAccountResult();
                // 若此数据为支付交易类型
                if (0 == payment.getBusinessType()){
                    paymentResult = paymentPayResult(payment);
                }
                // 若此数据为退款类型
                if (1 == payment.getBusinessType()){
                    paymentResult = paymentRefundResult(payment);
                }
                // 对账类型： 0.未对账（支付宝缺失记录）
                paymentResult.setCheckingType(0);
                paymentResults.add(paymentResult);
            }
        }else {
            paymentResults = null;
        }
        return paymentResults;
    }

    /**
     * 检验某个payId下的所有订单是否均为已完成状态
     * @param payId
     * @return
     */
    private Boolean checkOrderStatus(String payId){
        //订单未完成个数
        int count = purchaseDao.findOrdersStatus(payId);
        if (count>0){
            return false;
        }
        return true;
    }

    /**
     * 组装阿里的支付信息(缺少核账时间，对账类型，收款类型)
     * @param pay
     * @return
     */
    private CheckingAccountResult aliPayResult(CheckingAccountAlipay pay){
        CheckingAccountResult aliPayResult = new CheckingAccountResult();
        // 商户订单号
        aliPayResult.setPayId(pay.getPayId());
        // 支付流水号
        aliPayResult.setOrderPaymentNum(pay.getPaymentNum());
        // 支付时间为其完成时间
        aliPayResult.setPaymentTime(pay.getEndTime());
        // 商家实收金额
        aliPayResult.setPaymentMoney(pay.getPaidInMoney());
        // 记录ID
        aliPayResult.setRecordId(pay.getRecordId());
        aliPayResult.setCheckerId(pay.getCheckerId());
        aliPayResult.setCreatedAt(new Date());
        aliPayResult.setUpdatedAt(new Date());
        return aliPayResult;
    }

    /**
     * 组装阿里的退款信息(缺少核账时间，对账类型，收款类型)
     * @param pay
     * @return
     */
    private CheckingAccountResult aliRefundResult(CheckingAccountAlipay pay){
        CheckingAccountResult aliRefundResult = new CheckingAccountResult();
        // 商户订单号
        aliRefundResult.setPayId(pay.getPayId());
        // 支付流水号
        aliRefundResult.setOrderPaymentNum(pay.getPaymentNum());
        // 支付时间为其创建时间
        aliRefundResult.setPaymentTime(pay.getCreateTime());
        // 商家实收金额
        aliRefundResult.setPaymentMoney(pay.getPaidInMoney());
        // 记录ID
        aliRefundResult.setRecordId(pay.getRecordId());
        aliRefundResult.setCheckerId(pay.getCheckerId());
        aliRefundResult.setCreatedAt(new Date());
        aliRefundResult.setUpdatedAt(new Date());
        aliRefundResult.setDrawbackPrice(pay.getPaidInMoney());
        aliRefundResult.setDrawbackTime(pay.getCreateTime());
        return aliRefundResult;
    }

    /**
     * 组装平台的退款信息(缺少核账时间，对账类型)
     * @param payment
     * @return
     */
    private CheckingAccountResult paymentRefundResult(CheckingAccountPayment payment){
        CheckingAccountResult refundResult = new CheckingAccountResult();
        // 商户订单号
        refundResult.setPayId(payment.getPayId());
        // 支付流水号
        refundResult.setOrderPaymentNum(payment.getOrderPaymentNum());
        // 退款发起时间
        refundResult.setPaymentTime(payment.getRefundTimeCreate());
        // 商家实收金额为退款金额(用0减，求负)
        refundResult.setPaymentMoney(BigDecimal.ZERO.subtract(payment.getRefundFee()));
        // 收款类型
        refundResult.setReceiptType(0);
        refundResult.setDrawbackPrice(BigDecimal.ZERO.subtract(payment.getRefundFee()));
        refundResult.setDrawbackTime(payment.getRefundTimeCreate());
        // 记录ID
        refundResult.setRecordId(payment.getRecordId());
        refundResult.setCheckerId(payment.getCheckerId());
        refundResult.setCreatedAt(new Date());
        refundResult.setUpdatedAt(new Date());
        return refundResult;
    }

    /**
     * 组装平台的支付信息(缺少核账时间，对账类型)
     * @param payment
     * @return
     */
    private CheckingAccountResult paymentPayResult(CheckingAccountPayment payment){
        CheckingAccountResult paymentPayResult = new CheckingAccountResult();
        // 商户订单号
        paymentPayResult.setPayId(payment.getPayId());
        // 支付流水号
        paymentPayResult.setOrderPaymentNum(payment.getOrderPaymentNum());
        // 收款类型
        paymentPayResult.setReceiptType(0);
        // 支付时间为其完成时间
        paymentPayResult.setPaymentTime(payment.getPayTimeComplate());
        // 商家实收金额为实收
        paymentPayResult.setPaymentMoney(payment.getReceiptAmount());
        // 记录ID
        paymentPayResult.setRecordId(payment.getRecordId());
        paymentPayResult.setCheckerId(payment.getCheckerId());
        paymentPayResult.setCreatedAt(new Date());
        paymentPayResult.setUpdatedAt(new Date());
        return paymentPayResult;
    }

    /**
     * 数据匹配且没退款的支付数据
     * @param pay
     * @return
     */
    private CheckingAccountResult overAndNoRefund(CheckingAccountAlipay pay) {
        CheckingAccountResult accountResult = aliPayResult(pay);
        // 对账类型
        accountResult.setCheckingType(2);
        // 若订单均为终态，那么此数据为银货两讫
        boolean flag = checkOrderStatus(pay.getPayId());
        // 收款类型
        if (flag) {
            accountResult.setReceiptType(1);
            // 核帐时间
            accountResult.setCheckingTime(new Date());
        } else {
            accountResult.setReceiptType(0);
        }
        return accountResult;
    }


    /**
     * 数据匹配有退款的支付数据
     * @param pay
     * @return
     */
    private List<CheckingAccountResult> overAndHaveRefund(CheckingAccountAlipay pay,List<CheckingAccountAlipay> aliRefunds) {
        List<CheckingAccountResult> overAndHaveRefunds = new ArrayList<>();
        CheckingAccountResult accountResult = aliPayResult(pay);
        // 对账类型
        accountResult.setCheckingType(2);
        // 若订单均为终态，那么此数据为银货两讫
        boolean flag = checkOrderStatus(pay.getPayId());
        // 为确保核帐时间相同，新建时间
        Date time = new Date();
        // 收款类型
        if (flag) {
            accountResult.setReceiptType(1);
            // 核帐时间
            accountResult.setCheckingTime(time);
        } else {
            accountResult.setReceiptType(0);
        }
        overAndHaveRefunds.add(accountResult);
        // 退款信息
        for (CheckingAccountAlipay alipay:aliRefunds) {
            CheckingAccountResult checkResult = aliRefundResult(alipay);
            // 核帐时间
            checkResult.setCheckingTime(time);
            // 对账类型
            checkResult.setCheckingType(2);
            // 收款类型
            checkResult.setReceiptType(accountResult.getReceiptType());
            overAndHaveRefunds.add(checkResult);
        }
        return overAndHaveRefunds;
    }

    /**
     * 支付信息匹配上了，但是退款信息匹配不上
     * @param pay
     * @param aliRefunds
     * @param paymentRefunds
     * @return
     */
    private List<CheckingAccountResult> payOverAndRefundError(CheckingAccountAlipay pay,List<CheckingAccountAlipay> aliRefunds,List<CheckingAccountPayment> paymentRefunds){
        List<CheckingAccountResult> overAndHaveRefundError = new ArrayList<>();
        // 支付信息
        CheckingAccountResult accountResult = aliPayResult(pay);
        // 收款类型
        accountResult.setReceiptType(0);
        // 对账类型
        accountResult.setCheckingType(3);
        overAndHaveRefundError.add(accountResult);
        // 阿里无退款，平台有退款或者两者都有退款，取平台信息
        if (Ognl.CollectionIsNotEmpty(paymentRefunds)){
            for (CheckingAccountPayment payment:paymentRefunds) {
                CheckingAccountResult refundResult = paymentRefundResult(payment);
                // 对账类型 两边有数据，但是具体数据不一致
                refundResult.setCheckingType(3);
                overAndHaveRefundError.add(refundResult);
                }
            }
        // 阿里有退款，平台无退款，取阿里信息
        if (Ognl.CollectionIsNotEmpty(aliRefunds) && !Ognl.CollectionIsNotEmpty(paymentRefunds)){
            for (CheckingAccountAlipay aliPayRefund:aliRefunds) {
                CheckingAccountResult aliRefund = aliRefundResult(aliPayRefund);
                aliRefund.setCheckingType(3);
                aliRefund.setReceiptType(0);
                overAndHaveRefundError.add(aliRefund);
            }
        }
        return overAndHaveRefundError;
    }


    /**
     * 记录为支付信息且此支付信息与回调表中的支付信息对上
     * @param pay
     * @return
     */
    private List<CheckingAccountResult> isPayAndOver(CheckingAccountAlipay pay){
        List<CheckingAccountResult> results = new ArrayList<>();
        // 支付信息对上,取出阿里的退款记录和平台的退款记录
        List<CheckingAccountAlipay> aliRefunds= alipayDao.findRefund(pay);
        List<CheckingAccountPayment> paymentRefunds=paymentDao.findRefund(pay.getPayId(),pay.getPaymentNum(),pay.getRecordId());
        // 若2个都没退款，那么银货两讫条件已具备
        if ((!Ognl.CollectionIsNotEmpty(aliRefunds)) && (!Ognl.CollectionIsNotEmpty(paymentRefunds))){
            CheckingAccountResult accountResult = overAndNoRefund(pay);
            results.add(accountResult);
        }else if (Ognl.CollectionIsNotEmpty(aliRefunds) && Ognl.CollectionIsNotEmpty(paymentRefunds)){
            // 将阿里退款数据传入平台进行对账
            List<CheckingAccountPayment> checkRefunds= paymentDao.checkRefundType(aliRefunds);
            boolean flag1 = Ognl.CollectionIsNotEmpty(checkRefunds) && checkRefunds.size() == aliRefunds.size();
            // 将平台退款数据传入阿里进行对账
            List<CheckingAccountAlipay> checkRefund = alipayDao.checkRefund(paymentRefunds);
            boolean flag2 = Ognl.CollectionIsNotEmpty(checkRefund) && checkRefund.size() == paymentRefunds.size();
            // 若两个完全对上，那么银货两讫条件已具备,查询其下订单是否为终态，若是，银货两讫，若非，预收款
            if (flag1 && flag2){
                results = overAndHaveRefund(pay,aliRefunds);
            }else {
                // 支付信息
                CheckingAccountResult accountResult = aliPayResult(pay);
                // 收款类型
                accountResult.setReceiptType(0);
                // 对账类型
                accountResult.setCheckingType(3);
                results.add(accountResult);
                for (CheckingAccountPayment payment:paymentRefunds) {
                    CheckingAccountResult refundResult = paymentRefundResult(payment);
                    // 对账类型 两边有数据，但是具体数据不一致
                    refundResult.setCheckingType(3);
                    results.add(refundResult);
                }
            }
        }else {
            //支付信息匹配上了，但是退款信息匹配不上
            results = payOverAndRefundError(pay,aliRefunds,paymentRefunds);
        }
        return results;
    }

    /**
     * 记录为支付信息但此支付信息与回调表中的支付信息对不上
     * @param pay
     * @return
     */
    private List<CheckingAccountResult> isPayAndNotOver(CheckingAccountAlipay pay){
        List<CheckingAccountResult> results = new ArrayList<>();
        // 支付信息对不上，取出这个支付流水号和payId对应的支付及退款信息，按支付和退款的类别组装数据
        List<CheckingAccountPayment> payments = paymentDao.findPaidAndRefund(pay);
        for (CheckingAccountPayment payment:payments) {
            CheckingAccountResult paymentResult = new CheckingAccountResult();
            // 若此数据为支付交易类型
            if (0 == payment.getBusinessType()){
                paymentResult = paymentPayResult(payment);
            }
            // 若此数据为退款类型
            if (1 == payment.getBusinessType()){
                paymentResult = paymentRefundResult(payment);
            }
            // 对账类型： 0.未对账（支付宝缺失记录）
            paymentResult.setCheckingType(3);
            results.add(paymentResult);
        }
        return results;
    }

    /**
     * 将阿里数据中的某一条支付信息与平台的数据进行对账
     * @param pay
     * @return
     */
    private List<CheckingAccountResult> checkPay(CheckingAccountAlipay pay){
        List<CheckingAccountResult> results = new ArrayList<>();
        // 将支付信息进行对账
        List<CheckingAccountPayment> checkPaids = paymentDao.checkPaidTypeByAlipay(pay);
        // 若对账结果为1条，说明支付信息对上了，其余为没对上
        if (Ognl.CollectionIsNotEmpty(checkPaids) && 1 == checkPaids.size()){
            results = isPayAndOver(pay);
        }else {
            results = isPayAndNotOver(pay);
        }
        return results;
    }

    /**
     * 将阿里和平台都有的某一条阿里数据进行对账
     * @param pay
     * @return
     */
    private List<CheckingAccountResult> checkBothHave(CheckingAccountAlipay pay,String paidType,String refundType){
        List<CheckingAccountResult> results = new ArrayList<>();
        // 若此数据为支付交易类型
        if (paidType.equals(pay.getBusinessType())){
            results = checkPay(pay);
        }
        // 若此数据为退款类型
        if (refundType.equals(pay.getBusinessType())){
            List<CheckingAccountAlipay> alipays = alipayDao.findPaid(pay);
            // 此阿里退款数据存在支付交易记录
            if (Ognl.CollectionIsNotEmpty(alipays) && 1 == alipays.size()){
                results = checkPay(alipays.get(0));
            }else {
                // 若不存在支付记录，肯定对不上帐，那么取平台的所有数据进行组装
                results = isPayAndNotOver(pay);
            }
        }
        return results;
    }


    /**
     * 两者都有的数据进行处理
     * @param pays
     * @param paidType
     * @param refundType
     * @return
     */
    private List<CheckingAccountResult> checkAllHave(List<CheckingAccountAlipay> pays,String paidType,String refundType ){
        List<CheckingAccountResult> allHaveResults = new ArrayList<>();
        // 两者皆有的数据集合不为空，那么进行循环计算
        if (Ognl.CollectionIsNotEmpty(pays)){
            List<String> numList = new ArrayList<>();
            for (CheckingAccountAlipay pay:pays) {
                // 判断传入的支付流水号是否已对过帐，若已对过，那么跳过循环
                if (numList.contains(pay.getPaymentNum())){
                    continue;
                }else {
                    numList.add(pay.getPaymentNum());
                }
                // 对账并生成结果集
                List<CheckingAccountResult> resultList = checkBothHave(pay,paidType,refundType);
                for (CheckingAccountResult accountResult:resultList) {
                    allHaveResults.add(accountResult);
                }
            }
        }else {
            allHaveResults = null;
        }
        return allHaveResults;
    }

    /**
     * 将数据进行对账
     * @param aliHave
     * @param paymentHave
     * @param allHave
     * @return
     */
    private void checking(List<CheckingAccountAlipay> aliHave,List<CheckingAccountPayment> paymentHave,List<CheckingAccountAlipay> allHave){
        // 阿里数据的业务类型
        String paidType = "交易";
        String refundType = "退款";
        // 计算阿里有智购平台没有的数据的对账结果
        List<CheckingAccountResult> aliResults = aliExist(aliHave,paidType,refundType);
        // 计算阿里无智购平台有的数据的对账结果
        List<CheckingAccountResult> paymentResults = paymentExist(paymentHave);
        // 计算两部分都有的数据的对账结果
        List<CheckingAccountResult> allHaveResults = checkAllHave(allHave,paidType,refundType);
        //组装
        if (Ognl.CollectionIsNotEmpty(aliResults)){
            resultDao.saveResult(aliResults);
        }
        if (Ognl.CollectionIsNotEmpty(paymentResults)){
            resultDao.saveResult(paymentResults);
        }
        if (Ognl.CollectionIsNotEmpty(allHaveResults)){
            int n = 1000;
            Integer times = getTimes(allHaveResults.size(),n);
            for (int i = 0; i < times ; i++) {
                List<CheckingAccountResult> sublist=null;
                if((i+1)*n <= allHaveResults.size()){
                    sublist = allHaveResults.subList(i*n,(i+1)*n);
                }else {
                    sublist = allHaveResults.subList(i*n,allHaveResults.size());
                }
                // 将结果写入结果表
                resultDao.saveResult(sublist);
                logger.debug(" 将结果写入结果表 = saveResult==>>> :{}",sublist);
                logger.debug(" CheckingAccountResult ===>>>:{} ",sublist);
            }
        }
    }

}

